<?php

namespace StudyBuddy\Controllers;

use \StudyBuddy\WebPage;
use \StudyBuddy\Request;
use \StudyBuddy\String;

/**
 * Class responsible for
 * displaying the reset password
 * form, processing the form,
 * generating a new ramdom password
 * for user
 * and emailing it to user
 */
class Resetpwd extends WebPage {
    const TPL_SUCCESS = 'New password was just sent to your email %s';

    const SUBJECT = 'Your %1$s login information';

    const EMAIL_BODY = 'This email contains your login information for %1$s

Your login: %2$s
Your password is: %3$s

You are advised to store the above information in a safe place so that you
do not face any inconvenience in future.

You can also change your password after you log in. 
	';

    protected $aRequired = array('uid', 'r');
    protected $username;
    protected $email;
    protected $layoutID = 1;

    /**
     * Newly generated password
     * @var string
     */
    protected $newPwd;

    protected function main() {

        d('$this->newPwd: ' . $this->newPwd);

        $this->checkHacks()
                ->validateCode();
//		->generatePassword()
//		->savePassword()
//		->emailPwd();
        // reset pwd
        $this->oForm = new \StudyBuddy\Forms\Resetpwd($this->oRegistry);
        $this->oForm->uid = $this->oRequest['uid'];
        $this->oForm->r = $this->oRequest['r'];
        $this->oForm->formTitle = $this->aPageVars['title'] = $this->_('Password reset');

        if ($this->oForm->isSubmitted() && $this->oForm->validate()) {
            $this->saveNewPassword()->markCodeUsed();
//            if (!empty($this->email)) {
//                $this->emailPwd();
//            }

            $this->aPageVars['body'] = '<div id="tools">' . $this->_('Password updated successfully') . '</div>';
        } else {
            $this->aPageVars['body'] = $this->oForm->getForm();
        }
        // end
//		$this->aPageVars['title'] = 'Password reset';
//		$this->aPageVars['body'] = '<div class="frm1">'.sprintf(self::TPL_SUCCESS, $this->email).'</div>';
    }

    protected function generatePassword() {
        $this->newPwd = String::makePasswd();

        return $this;
    }

    /**
     *
     * Update USERS collection with the
     * new value of salted password
     *
     * @param string $pwd
     * @param int $uid
     *
     * @return object $this
     */
    protected function savePassword() {
        d('$this->newPwd: ' . $this->newPwd);

        $salted = String::hashPassword($this->newPwd);
        $newdata = array('$set' => array("pwd" => $salted));

        $this->oRegistry->Mongo->USERS->update(array('_id' => (int) $this->oRequest['uid']), $newdata);

        return $this;
    }

    /**
     * Update ['pwd'] in Viewer object and save object
     *
     * @return object $this
     */
    protected function saveNewPassword() {
        $this->newPwd = $this->oRequest['pwd1'];
        d('$this->newPwd: ' . $this->newPwd);

        $salted = String::hashPassword($this->newPwd);
        $newdata = array('$set' => array("pwd" => $salted));

        $this->oRegistry->Mongo->USERS->update(array('_id' => (int) $this->oRequest['uid']), $newdata);

        return $this;
    }

    /**
     * Checks that supplied password reset code
     * is valid and matches the supplied uid
     * and is not older than 24 hours
     *
     * Up to 10 results will be selected
     * and each one will be tested untill
     * a positive match is found.
     *
     * This is so that
     * if a user requested a password reset link
     * several times
     * (maybe forgot to take his medications or something like that)
     * then any one of the requested codes sent to user will be considered valid.
     *
     * @return object $this
     *
     * @throws StudyBuddyException in case the
     * supplied code is invalid OR older than 24 hours
     *
     */
    protected function validateCode() {
        $timeOffset = (time() - 86500);
        $uid = (int) $this->oRequest['uid'];

        $aResult = $this->oRegistry->Mongo->PASSWORD_CHANGE
                ->findOne(array('_id' => $this->oRequest['r'], 'i_uid' => $uid));

        d('$aResult ' . print_r($aResult, true));

        if (empty($aResult)) {

            $this->saveFailedAttempt()->oRegistry->Dispatcher->post($this, 'onFailedPasswordReset');

            throw new \StudyBuddy\Exception('wrong password reset code');
        }


        if ($aResult['i_ts'] < $timeOffset) {
            d('code expired');

            throw new \StudyBuddy\Exception('password_reset_code_expired');
        }


        if (!empty($aResult['i_used'])) {
            d('code used');

            throw new \StudyBuddy\Exception('This password reset link was already used on ' . date('r', $aResult['i_used']));
        }

        $aVal = $this->oRegistry->Mongo->USERS
                ->findOne(array('_id' => (int) $aResult['i_uid']));

        $this->username = $aVal['username'];
        $this->email = $aVal['email'];
//		$this->markCodeUsed();

        return $this;
    }

    /**
     * Once the password reset code has been
     * validated we should delete it so that it cannot
     * be reused. This is both for security reason
     * and so that the same user cannot accidentely change
     * the password again by clicking on the
     * same link in email
     *
     * @return object $this
     */
    protected function markCodeUsed() {
        $newdata = array('$set' => array('i_used' => time()));

        $this->oRegistry->Mongo->PASSWORD_CHANGE
                ->update(array('_id' => $this->oRequest['r']), $newdata);

        return $this;
    }

    /**
     * Saves geodata from where the failed
     * reset password attempt came from
     *
     * @return object $this
     */
    protected function saveFailedAttempt() {
        $ip = Request::getIP();

        $aData = array(
            'i_uid' => (int) $this->oRequest['uid'],
            'i_ts' => time());

        $res = $this->saveResourceLocation('1', $ip, $aData, 'PASSWORD_CHANGE');

        d('$res: ' . $res);

        return $this;
    }

    /**
     * Check for previous
     * failed attempts to reset password
     * by using incorrect code
     *
     * @throws StudyBuddyException in case
     * over 5 previous failed attempts
     * in past 24 hours
     * detected from
     * the same ip address or for the same
     * userid
     *
     * @return object $this
     */
    protected function checkHacks() {
        $ipHacks = 0;
        $uidHacks = 0;

        $timeOffset = time() - 86400;
        $cur = $this->oRegistry->Mongo->PASSWORD_CHANGE
                ->find(array('i_ts' > $timeOffset));

        if ($cur && ($cur->count(true) > 0)) {

            $ip = Request::getIP();

            foreach ($cur as $aVal) {
                if ($ip == $aVal['ip']) {
                    $ipHacks += 1;
                }

                if ($this->oRequest['uid'] == $aVal['i_uid']) {
                    $uidHacks += 1;
                }

                if ($uidHacks > 5 || $ipHacks > 5) {
                    e('StudyBuddyError: hacking of password reset link. $uidHacks: ' . $uidHacks . ' $ipHacks: ' . $ipHacks . ' from ip: ' . $ip);

                    $this->oRegistry->Dispatcher->post($this, 'onPasswordResetHack', $aVal);

                    throw new \StudyBuddy\Exception('access_denied');
                }
            }
        }

        return $this;
    }

    /**
     * Send out the new password to user
     *
     */
    protected function emailPwd() {
        $body = vsprintf(self::EMAIL_BODY, array($this->oRegistry->Ini->SITE_NAME, $this->username, $this->newPwd));
        $subject = sprintf(self::SUBJECT, $this->oRegistry->Ini->SITE_NAME);

        \StudyBuddy\Mailer::factory($this->oRegistry)->mail($this->email, $subject, $body);

        return $this;
    }

}
